﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Netbits.Common.Collections;
using Netbits.Common.Conditions;
using Netbits.Common.Threading;

namespace Netbits.Common.Logging
{
	/// <summary>
	/// Represents the LogManager that holds all logs and groups
	/// </summary>
	public sealed class LogManager : IDisposable
	{
		#region Private Variables

		LockedListExtended<LogEntry> _Logs;
		LockedDictionary<string, LogGroup> _Groups;
		LockedList<ILogHandler> _SyncLogHandler;
		LockedListExtended<ILogHandler> _AsyncLogHandler;
		LockedQueue<LogEntry> _AsyncLogs;
		Lock _Lock;

		ThreadEntry _AsynHandlerThread = null;
		int _IsCachingDisabled = 0;
		bool _Disposed;

		#endregion

		#region Constructor

		/// <summary>
		/// Create a new instance of LogManager
		/// </summary>
		public LogManager()
		{
			_Logs = new LockedListExtended<LogEntry>();
			_Logs.MaxItems = 1000;
			_Groups = new LockedDictionary<string, LogGroup>();
			_SyncLogHandler = new LockedList<ILogHandler>();
			_AsyncLogHandler = new LockedListExtended<ILogHandler>();
			_AsyncLogs = new LockedQueue<LogEntry>();
			_Lock = new Lock();
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns a list of all log items.
		/// </summary>
		public IExtended<ListExtended<LogEntry>, LogEntry> Logs
		{
			get
			{
				return _Logs;
			}
		}

		/// <summary>
		/// Get or sets if the log-item caching is enabled/disabled
		/// </summary>
		public bool IsCachingDisabled
		{
			get
			{
				return (_IsCachingDisabled > 0);
			}
			set
			{
				System.Threading.Interlocked.Exchange( ref _IsCachingDisabled, (value == true ? 1 : 0) );
			}
		}

		/// <summary>
		/// Get or sets the maximum cached log-items per group
		/// </summary>
		public int MaxCache
		{
			get
			{
				return _Logs.MaxItems;
			}
			set
			{
				_Logs.MaxItems = value;
				foreach( KeyValuePair<string, LogGroup> lg in _Groups )
					lg.Value.MaxCache = value;
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Add new ILogHandler instance.
		/// </summary>
		/// <param name="Handler_">The ILogHandler instance</param>
		public void AddLogHandler( ILogHandler Handler_ )
		{
			Condition.Requires( Handler_, "Handler_" ).IsNotNull();

			if( Handler_.HandleSync )
				_SyncLogHandler.Add( Handler_ );
			else
				_AsyncLogHandler.Add( Handler_ );

			if( _AsyncLogHandler.Count > 0 )
				_StartAsyncHandler();
		}

		/// <summary>
		/// Remove existing ILogHandler instance.
		/// </summary>
		/// <param name="Handler_">The ILogHandler instance</param>
		public void RemoveLogHandler( ILogHandler Handler_ )
		{
			Condition.Requires( Handler_, "Handler_" ).IsNotNull();

			if( _SyncLogHandler.Contains( Handler_ ) )
				_SyncLogHandler.Remove( Handler_ );
			else
				_AsyncLogHandler.Remove( Handler_ );

			if( _AsyncLogHandler.Count == 0 )
				_StopAsyncHandler();
		}

		/// <summary>
		/// Gets the LogGroup named by current initialized class.
		/// </summary>
		/// <remarks>If the LogGroup does exists a reference are returned (only single instance)</remarks>
		/// <returns>The LogGroup</returns>
		public LogGroup GetGroup()
		{
			StackFrame sf = new StackFrame( 1, false );
			return GetGroup( sf.GetMethod().DeclaringType.FullName );
		}

		/// <summary>
		/// Gets the LogGroup named by the specified initialized type.
		/// </summary>
		/// <param name="Type_">The type</param>
		/// <remarks>If the LogGroup does exists a reference are returned (only single instance)</remarks>
		/// <returns>The LogGroup</returns>
		public LogGroup GetGroup( Type Type_ )
		{
			return GetGroup( Type_.FullName );
		}

		/// <summary>
		/// Gets the LogGroup named by specified name
		/// </summary>
		/// <param name="Name_">The name of the LogGroup</param>
		/// <remarks>If the LogGroup does exists a reference are returned (only single instance)</remarks>
		/// <returns>The LogGroup</returns>
		public LogGroup GetGroup( string Name_ )
		{
			try
			{
				_Lock.EnterUpgradeableReadLock();
				LogGroup lg = _Groups[Name_];
				if( lg != null )
					return lg;

				lg = new LogGroup( this, Name_ );
				lg.MaxCache = MaxCache;
				try
				{
					_Lock.EnterWriteLock();
					_Groups.Add( Name_, lg );
					return lg;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
			finally
			{
				_Lock.ExitUpgradeableReadLock();
			}
		}

		/// <summary>
		/// Gets the default LogGroup
		/// </summary>
		/// <returns>The LogGroup</returns>
		public LogGroup GetDefaultGroup()
		{
			return GetGroup( "default" );
		}

		#endregion

		#region Internal Functions

		internal void __Add( LogEntry Item_ )
		{
			if( _IsCachingDisabled == 0 )
				_Logs.Add( Item_ );

			if( _AsyncLogHandler.Count > 0 )
				_AsyncLogs.Enqueue( Item_ );

			if( _SyncLogHandler.Count > 0 )
			{
				foreach( ILogHandler handler in _SyncLogHandler )
					handler.ProcessLog( Item_ );
			}
		}

		internal void __Remove( LogEntry Item_ )
		{
			_Logs.Remove( Item_ );
		}

		#endregion

		#region Private Functions

		void _StartAsyncHandler()
		{
			try
			{
				_Lock.EnterUpgradeableReadLock();

				if( _AsynHandlerThread != null )
					return;

				try
				{
					_Lock.EnterWriteLock();
					_AsynHandlerThread = ThreadManager.Create( "LogManager-Asynchandler" );
					_AsynHandlerThread.HandleNextStep = _AsyncLogHandler.WaitHandler();
					_AsynHandlerThread.IsBackground = true;
					_AsynHandlerThread.Priority = System.Threading.ThreadPriority.AboveNormal;
					_AsynHandlerThread.Run = _AsyncHandler;
					_AsynHandlerThread.Start();
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
			finally
			{
				_Lock.ExitUpgradeableReadLock();
			}
		}

		void _StopAsyncHandler()
		{
			try
			{
				_Lock.EnterUpgradeableReadLock();
				if( _AsynHandlerThread == null )
					return;

				try
				{
					_Lock.EnterWriteLock();
					_AsynHandlerThread.Stop();
					_AsynHandlerThread.Dispose();
					_AsynHandlerThread = null;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
			finally
			{
				_Lock.ExitUpgradeableReadLock();
			}
		}

		void _AsyncHandler( ThreadEntry Thread_, object[] Params_ )
		{
			LogEntry le = _AsyncLogs.Dequeue();
			if( le == null )
				return;

			foreach( ILogHandler handler in _AsyncLogHandler )
				handler.ProcessLog( le );
		}

		#endregion

		#region IDisposable

		void Dispose( bool Disposing_ )
		{
			if( !_Disposed )
			{
				if( Disposing_ )
				{
					if( _Logs != null )
						_Logs.Dispose();

					if( _Groups != null )
						_Groups.Dispose();

					if( _AsyncLogHandler != null )
						_AsyncLogHandler.Dispose();

					if( _Lock != null )
						_Lock.Dispose();
				}

				_Disposed = true;
			}
		}


		/// <summary>
		/// IDisposable implementation. 
		/// </summary>
		public void Dispose()
		{
			_StopAsyncHandler();
		}

		#endregion
	}
}
